/*
 *
 *  * Copyright 2020 New Relic Corporation. All rights reserved.
 *  * SPDX-License-Identifier: Apache-2.0
 *
 */

package com.newrelic.agent.utilization;

import com.newrelic.agent.Agent;
import com.newrelic.agent.MetricNames;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

public class GCP implements CloudVendor {
    public static String PROVIDER = "gcp";
    private final CloudUtility cloudUtility;

    public GCP(CloudUtility cloudUtility) {
        this.cloudUtility = cloudUtility;
    }

    protected static final String INSTANCE_DOCUMENT_RECURSIVE = "http://metadata.google.internal/computeMetadata/v1/instance/?recursive=true";
    private static final int REQUEST_TIMEOUT_MILLIS = 100;

    // GCP map keys. These are the keys that will be added to the vendor hash in the JSON generated by the agent.
    private static final String GCP_ID_KEY = "id";
    private static final String GCP_MACHINE_TYPE_KEY = "machineType";
    private static final String GCP_ZONE_KEY = "zone";
    private static final String GCP_NAME_KEY = "name";

    @Override
    public GcpData getData() {
        try {
            String unparsedResultRecurse = getGcpValues();

            JSONParser jsonParser = new JSONParser();
            JSONObject result = null;

            if (unparsedResultRecurse != null) {
                result = (JSONObject) jsonParser.parse(unparsedResultRecurse);
            }

            if (result == null || result.isEmpty()) {
                return GcpData.EMPTY_DATA;
            }

            String id = String.valueOf(result.get(GCP_ID_KEY));
            String machineType = (String) result.get(GCP_MACHINE_TYPE_KEY);
            String name = (String) result.get(GCP_NAME_KEY);
            String zone = (String) result.get(GCP_ZONE_KEY);

            if (cloudUtility.isInvalidValue(id) || cloudUtility.isInvalidValue(machineType)
                    || cloudUtility.isInvalidValue(name) || cloudUtility.isInvalidValue(zone)) {
                Agent.LOG.log(Level.WARNING, "Failed to validate GCP value");
                recordGcpError();
                return GcpData.EMPTY_DATA;
            }

            machineType = machineType.substring(machineType.lastIndexOf('/') + 1);
            zone = zone.substring(zone.lastIndexOf('/') + 1);

            GcpData data = new GcpData(id, machineType, name, zone);
            Agent.LOG.log(Level.FINEST, "Found {0}", data);
            return data;
        } catch (Exception e) {
            return GcpData.EMPTY_DATA;
        }
    }

    protected String getGcpValues() {
        try {
            return cloudUtility.httpGet(INSTANCE_DOCUMENT_RECURSIVE, REQUEST_TIMEOUT_MILLIS, "Metadata-Flavor: Google");
        } catch (Throwable t) {
            Agent.LOG.log(Level.FINEST, MessageFormat.format("Error occurred trying to get GCP values. {0}", t));
            recordGcpError();
        }
        return null;
    }

    private void recordGcpError() {
        cloudUtility.recordError(MetricNames.SUPPORTABILITY_GCP_ERROR);
    }

    protected static class GcpData implements CloudData {
        private final String id;
        private final String machineType;
        private final String name;
        private final String zone;

        static final GcpData EMPTY_DATA = new GcpData();

        private GcpData() {
            id = null;
            machineType = null;
            name = null;
            zone = null;
        }

        protected GcpData(String id, String machineType, String name, String zone) {
            this.id = id;
            this.machineType = machineType;
            this.name = name;
            this.zone = zone;
        }

        public String getId() {
            return id;
        }

        public String getMachineType() {
            return machineType;
        }

        public String getName() {
            return name;
        }

        public String getZone() {
            return zone;
        }

        @Override
        public Map<String, String> getValueMap() {
            Map<String, String> gcp = new HashMap<>();

            if (machineType == null || id == null || zone == null || name == null) {
                return gcp;
            } else {
                gcp.put(GCP_MACHINE_TYPE_KEY, machineType);
                gcp.put(GCP_ID_KEY, id);
                gcp.put(GCP_ZONE_KEY, zone);
                gcp.put(GCP_NAME_KEY, name);
            }
            return gcp;
        }

        @Override
        public String getProvider() {
            return PROVIDER;
        }

        @Override
        public boolean isEmpty() {
            return this == EMPTY_DATA;
        }

        @Override
        public String toString() {
            return "GcpData{" +
                    "id='" + id + '\'' +
                    ", machineType='" + machineType + '\'' +
                    ", name='" + name + '\'' +
                    ", zone='" + zone + '\'' +
                    '}';
        }
    }

}
